#pragma once

#include "LockGuard.hpp"
#include <iostream>
#include <fstream>
#include <ctime>
#include <cstdarg>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <pthread.h>

namespace log_ns
{
    enum
    {
        Monday = 1,
        Tuesday,
        Wednesday,
        Thursday,
        Friday,
        Saturday,
        Sunday = 0
    };

    enum
    {
        DEBUG = 1,
        INFO,
        WARNING,
        ERROR,
        FATAL
    };

    class logmessge
    {
    public:
        std::string _level;
        pid_t _id;
        std::string _filename;
        int _filenumber;
        std::string _curr_time;
        std::string _message_info;
    };

#define SCREEN_TYPE 1
#define FILE_TYPE 2
#define RELEASE_MODE 3
#define DEBUG_MODE 4

    const std::string glogfile = "./log.txt";

    class Log
    {
    public:
        Log(const std::string &logfile = glogfile, int type = SCREEN_TYPE, int mode = DEBUG_MODE)
            : _logfile(logfile), _type(type), _mode(mode)
        {
            pthread_mutex_init(&_mutex, nullptr);
        }
        void Enable(int type)
        {
            LockGuard lockguard(&_mutex);
            _type = type;
        }
        void LogMessaage(std::string filename, int filenumber, int level, const char *format, ...)
        {
            logmessge lg;
            lg._level = LevelToString(level);
            lg._id = getpid();
            lg._filename = filename;
            lg._filenumber = filenumber;
            lg._curr_time = GetCurrTime();

            va_list ap;
            va_start(ap, format);
            char log_info[1024];
            vsnprintf(log_info, sizeof log_info, format, ap);
            va_end(ap);
            lg._message_info = log_info;

            // 打印出来日志
            FlushLog(lg);
        }
        ~Log()
        {
            pthread_mutex_destroy(&_mutex);
        }

    private:
        void FlushLogToScreen(const logmessge &lg)
        {
            printf("[%s][%d][%s][%d][%s] %s",
                   lg._level.c_str(),
                   lg._id,
                   lg._filename.c_str(),
                   lg._filenumber,
                   lg._curr_time.c_str(),
                   lg._message_info.c_str());
        }
        void FlushLogToFile(const logmessge &lg)
        {
            std::ofstream out(_logfile, std::ios::app);
            if (!out.is_open())
                return;
            char logtxt[2048];
            snprintf(logtxt, sizeof logtxt, "[%s][%d][%s][%d][%s] %s",
                     lg._level.c_str(),
                     lg._id,
                     lg._filename.c_str(),
                     lg._filenumber,
                     lg._curr_time.c_str(),
                     lg._message_info.c_str());
            out.write(logtxt, strlen(logtxt));
            out.close();
        }
        void FlushLog(const logmessge &lg)
        {
            // 加过滤逻辑
            // if (lg._level == "DEBUG" && _mode == RELEASE_MODE) // 无须打印调试信息
            LockGuard lockguard(&_mutex);
            switch (_type)
            {
            case SCREEN_TYPE:
                FlushLogToScreen(lg);
                break;
            case FILE_TYPE:
                FlushLogToFile(lg);
                break;
            default:
                FlushLogToScreen(lg);
                break;
            }
        }
        std::string WeekDay(int wday)
        {
            switch (wday)
            {
            case Monday:
                return "一";
            case Tuesday:
                return "二";
            case Wednesday:
                return "三";
            case Thursday:
                return "四";
            case Friday:
                return "五";
            case Saturday:
                return "六";
            case Sunday:
                return "天";
            default:
                return "错误";
            }
        }
        std::string GetCurrTime()
        {
            time_t now = time(nullptr);
            struct tm *curr_time = localtime(&now);
            char buffer[128];
            const char *weekday = WeekDay(curr_time->tm_wday).c_str();
            snprintf(buffer, sizeof buffer, "%4d-%02d-%02d %02d:%02d:%02d 星期%s",
                     curr_time->tm_year + 1900,
                     curr_time->tm_mon + 1,
                     curr_time->tm_mday,
                     curr_time->tm_hour,
                     curr_time->tm_min, curr_time->tm_sec,
                     weekday);
            return buffer;
        }
        std::string LevelToString(int level)
        {
            switch (level)
            {
            case DEBUG:
                return "DEBUG";
            case INFO:
                return "INFO";
            case WARNING:
                return "WARNING";
            case ERROR:
                return "ERROR";
            case FATAL:
                return "FATAL";
            default:
                return "UNKOWN";
            }
        }

    private:
        int _type;
        int _mode;
        std::string _logfile;
        pthread_mutex_t _mutex;
    };

    Log lg;

#define LOG(Level, Format, ...)                                           \
    do                                                                    \
    {                                                                     \
        lg.LogMessaage(__FILE__, __LINE__, Level, Format, ##__VA_ARGS__); \
    } while (0)
#define EnableScreen()          \
    do                          \
    {                           \
        lg.Enable(SCREEN_TYPE); \
    } while (0)
#define EnableFile()          \
    do                        \
    {                         \
        lg.Enable(FILE_TYPE); \
    } while (0)
};